// Copyright (c) 2022. Eritque arcus and contributors.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or any later version(in your opinion).
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#pragma once

#include <cstddef>  // ptrdiff_t
#include <iterator> // reverse_iterator
#include <utility>  // declval

#include <nlohmann/detail/abi_macros.hpp>

NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail {

    //////////////////////
    // reverse_iterator //
    //////////////////////

    /*!
@brief a template for a reverse iterator class

@tparam Base the base iterator type to reverse. Valid types are @ref
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
create @ref const_reverse_iterator).

@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
  The iterator that can be moved can be moved in both directions (i.e.
  incremented and decremented).
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
  It is possible to write to the pointed-to element (only if @a Base is
  @ref iterator).

@since version 1.0.0
*/
    template<typename Base>
    class json_reverse_iterator : public std::reverse_iterator<Base> {
    public:
        using difference_type = std::ptrdiff_t;
        /// shortcut to the reverse iterator adapter
        using base_iterator = std::reverse_iterator<Base>;
        /// the reference type for the pointed-to element
        using reference = typename Base::reference;

        /// create reverse iterator from iterator
        explicit json_reverse_iterator(const typename base_iterator::iterator_type &it) noexcept
            : base_iterator(it) {}

        /// create reverse iterator from base class
        explicit json_reverse_iterator(const base_iterator &it) noexcept : base_iterator(it) {}

        /// post-increment (it++)
        json_reverse_iterator operator++(int) & // NOLINT(cert-dcl21-cpp)
        {
            return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
        }

        /// pre-increment (++it)
        json_reverse_iterator &operator++() {
            return static_cast<json_reverse_iterator &>(base_iterator::operator++());
        }

        /// post-decrement (it--)
        json_reverse_iterator operator--(int) & // NOLINT(cert-dcl21-cpp)
        {
            return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
        }

        /// pre-decrement (--it)
        json_reverse_iterator &operator--() {
            return static_cast<json_reverse_iterator &>(base_iterator::operator--());
        }

        /// add to iterator
        json_reverse_iterator &operator+=(difference_type i) {
            return static_cast<json_reverse_iterator &>(base_iterator::operator+=(i));
        }

        /// add to iterator
        json_reverse_iterator operator+(difference_type i) const {
            return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
        }

        /// subtract from iterator
        json_reverse_iterator operator-(difference_type i) const {
            return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
        }

        /// return difference
        difference_type operator-(const json_reverse_iterator &other) const {
            return base_iterator(*this) - base_iterator(other);
        }

        /// access to successor
        reference operator[](difference_type n) const {
            return *(this->operator+(n));
        }

        /// return the key of an object iterator
        auto key() const -> decltype(std::declval<Base>().key()) {
            auto it = --this->base();
            return it.key();
        }

        /// return the value of an iterator
        reference value() const {
            auto it = --this->base();
            return it.operator*();
        }
    };

} // namespace detail
NLOHMANN_JSON_NAMESPACE_END
